compat/__pycache__/__init__.cpython-39.pyc000064400000000247151061232500014425 0ustar00a \f@sdS)NrrrL/usr/local/lib/python3.9/site-packages/importlib_metadata/compat/__init__.pycompat/__pycache__/py39.cpython-39.pyc000064400000002216151061232500013470 0ustar00a \fN@s^dZddlmZmZmZer.ddlmZmZneZZeeedddZ ee dd d Z d S) z) Compatibility layer with Python 3.8/3.9 ) TYPE_CHECKINGAnyOptional) Distribution EntryPoint)distreturnc CsHz|jWStyBddlm}|t|ddp:|jdYS0dS)z] Honor name normalization for distributions that don't provide ``_normalized_name``. r)PreparednameNName)Z_normalized_nameAttributeErrorr normalizegetattrmetadata)rr rH/usr/local/lib/python3.9/site-packages/importlib_metadata/compat/py39.pynormalized_names   r)epr cKsTz|jfi|WStyNddlm}||j|j|jjfi|YS0dS)zO Workaround for ``EntryPoint`` objects without the ``matches`` method. r)rN)matchesr rrr valuegroup)rparamsrrrr ep_matchess   rN) __doc__typingrrrrrrstrrboolrrrrrs  compat/__init__.py000064400000000000151061232500010121 0ustar00compat/py39.py000064400000002116151061232500007200 0ustar00""" Compatibility layer with Python 3.8/3.9 """ from typing import TYPE_CHECKING, Any, Optional if TYPE_CHECKING: # pragma: no cover # Prevent circular imports on runtime. from .. import Distribution, EntryPoint else: Distribution = EntryPoint = Any def normalized_name(dist: Distribution) -> Optional[str]: """ Honor name normalization for distributions that don't provide ``_normalized_name``. """ try: return dist._normalized_name except AttributeError: from .. import Prepared # -> delay to prevent circular imports. return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) def ep_matches(ep: EntryPoint, **params) -> bool: """ Workaround for ``EntryPoint`` objects without the ``matches`` method. """ try: return ep.matches(**params) except AttributeError: from .. import EntryPoint # -> delay to prevent circular imports. # Reconstruct the EntryPoint object to make sure it is compatible. return EntryPoint(ep.name, ep.value, ep.group).matches(**params) __pycache__/__init__.cpython-39.pyc000064400000117272151061232500013151 0ustar00a \f@sddlmZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlZddlZddlmZmZddlmZddlmZmZddlmZmZddlmZm Z dd l!m"Z"m#Z#dd lm$Z$m%Z%dd l&m'Z'dd l(m)Z)dd l*m+Z+ddlm,Z,ddl-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5gdZ6Gddde7Z8GdddZ9GdddZ:Gddde;ZGdddZ?GdddZ@Gdd d e@ZAGd!d"d"e+ZBGd#d$d$ZCGd%d&d&ZDGd'd(d(ZEeGd)d*d*eeBZFGd+d,d,eAZGd-d d.d/d0ZHd1d2d3d4ZId-d5d.d6d7ZJd-d-d.d8d9ZKejLe#ejMd:ZNdd2d;d<ZOd-d=d.d>d?ZPd-d@d.dAdBZQdCd2dDdEZRdFdGZSddHdIdJdKZTdd-dIdLdMZUdNdOZVdS)P) annotationsN) _adapters_meta)py39)FreezableDefaultDictPair) NullFinderinstall) method_cache pass_none)always_iterableunique_everseen)PackageMetadata SimplePath)suppress) import_module)MetaPathFinder)starmap)AnyIterableListMappingMatchOptionalSetcast) DistributionDistributionFinderrPackageNotFoundError distribution distributions entry_pointsfilesmetadatapackages_distributionsrequiresversionc@s0eZdZdZddddZeddddZdS) rzThe package was not found.strreturncCs d|jS)Nz"No package metadata was found for nameselfr/E/usr/local/lib/python3.9/site-packages/importlib_metadata/__init__.py__str__9szPackageNotFoundError.__str__cCs |j\}|SN)argsr.r,r/r/r0r,<szPackageNotFoundError.nameN)__name__ __module__ __qualname____doc__r1propertyr,r/r/r/r0r6src@sJeZdZdZedZeddZ e d ddZ e dd d d Z dS) Sectioneda A simple entry point config parser for performance >>> for item in Sectioned.read(Sectioned._sample): ... print(item) Pair(name='sec1', value='# comments ignored') Pair(name='sec1', value='a = 1') Pair(name='sec1', value='b = 2') Pair(name='sec2', value='a = 2') >>> res = Sectioned.section_pairs(Sectioned._sample) >>> item = next(res) >>> item.name 'sec1' >>> item.value Pair(name='a', value='1') >>> item = next(res) >>> item.value Pair(name='b', value='2') >>> item = next(res) >>> item.name 'sec2' >>> item.value Pair(name='a', value='2') >>> list(res) [] zm [sec1] # comments ignored a = 1 b = 2 [sec2] a = 2 cCsdd|j||jdDS)Ncss,|]$}|jdur|jt|jdVqdS)N)value)r,_replacerparser;).0sectionr/r/r0 ms z*Sectioned.section_pairs..)filter_)readvalid)clstextr/r/r0 section_pairskszSectioned.section_pairsNccsXt|ttj|}d}|D]4}|do4|d}|rF|d}qt||VqdS)N[]z[])filtermapr(strip splitlines startswithendswithr)rErAlinesr,r;Z section_matchr/r/r0rBss zSectioned.readr(linecCs|o|d S)N#)rMrPr/r/r0rC~szSectioned.valid)N) r5r6r7r8textwrapdedentlstripZ_sample classmethodrF staticmethodrBrCr/r/r/r0r:Bs   r:c@seZdZUdZedZded<ded<ded<dZded <dddd d d d Z ddddZ e ddddZ e ddddZ e ddddZddZddZddZdd Zd!d"Zd#d$Zd%d&Zd'dd(d)ZdS)* EntryPointaAn entry point as defined by Python packaging conventions. See `the packaging docs on entry points `_ for more information. >>> ep = EntryPoint( ... name=None, group=None, value='package.module:attr [extra1, extra2]') >>> ep.module 'package.module' >>> ep.attr 'attr' >>> ep.extras ['extra1', 'extra2'] zH(?P[\w.]+)\s*(:\s*(?P[\w.]+)\s*)?((?P\[.*\])\s*)?$r(r,r;groupNzOptional[Distribution]distNone)r,r;rYr*cCst|j|||ddS)Nr,r;rYvarsupdate)r.r,r;rYr/r/r0__init__szEntryPoint.__init__rr)cCsJtt|j|j}t|d}td|dp2dd}t t ||S)zLoad the entry point from its definition. If only a module is indicated by the value, return that module. Otherwise, return the named object. moduleNattr.) rrpatternmatchr;rrYrIsplit functoolsreducegetattr)r.rfraattrsr/r/r0loadszEntryPoint.loadcCs$|j|j}|dusJ|dS)Nrarerfr;rYr.rfr/r/r0ras zEntryPoint.modulecCs$|j|j}|dusJ|dS)Nrbrmrnr/r/r0rbs zEntryPoint.attr List[str]cCs0|j|j}|dusJtd|dp,dS)Nz\w+extrasrc)rerfr;refindallrYrnr/r/r0rps zEntryPoint.extrascCst|j|d|S)NrZr])r.rZr/r/r0_forszEntryPoint._forc s(fdd|D}tttj||S)a$ EntryPoint matches the given parameters. >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') >>> ep.matches(group='foo') True >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') True >>> ep.matches(group='foo', name='other') False >>> ep.matches() True >>> ep.matches(extras=['extra1', 'extra2']) True >>> ep.matches(module='bing') True >>> ep.matches(attr='bong') True c3s|]}t|VqdSr2rj)r>paramr-r/r0r@z%EntryPoint.matches..)allrJoperatoreqvalues)r.paramsrkr/r-r0matchesszEntryPoint.matchescCs|j|j|jfSr2r\r-r/r/r0_keyszEntryPoint._keycCs||kSr2r~r.otherr/r/r0__lt__szEntryPoint.__lt__cCs||kSr2rrr/r/r0__eq__szEntryPoint.__eq__cCs tddS)Nz!EntryPoint objects are immutable.)AttributeError)r.r,r;r/r/r0 __setattr__szEntryPoint.__setattr__cCsd|jd|jd|jdS)NzEntryPoint(name=z, value=z, group=)r\r-r/r/r0__repr__szEntryPoint.__repr__intcCs t|Sr2)hashr~r-r/r/r0__hash__szEntryPoint.__hash__)r5r6r7r8rqcompilere__annotations__rZr`rlr9rarbrprtr}r~rrrrrr/r/r/r0rXs0   rXc@sveZdZdZdZdddddZdd Zdd d d Zed d ddZ ed d ddZ e ddZ e ddZdS) EntryPointszC An immutable collection of selectable EntryPoint objects. r/r(rXr,r*cCs6ztt|j|dWSty0t|Yn0dS)z; Get the EntryPoint in self matching name. r+N)nextiterselect StopIterationKeyErrorr4r/r/r0 __getitem__s zEntryPoints.__getitem__cCsd|jjt|fS)zz Repr with classname and tuple constructor to signal that we deviate from regular tuple behavior. z%s(%r)) __class__r5tupler-r/r/r0rszEntryPoints.__repr__r)c stfdd|DS)zv Select entry points from self that match the given parameters (typically group and/or name). c3s$|]}tj|fir|VqdSr2)rZ ep_matchesr>epr|r/r0r@rwz%EntryPoints.select..)r)r.r|r/rr0rszEntryPoints.selectzSet[str]cCsdd|DS)zB Return the set of all names of all entry points. cSsh|] }|jqSr/r+rr/r/r0 "rwz$EntryPoints.names..r/r-r/r/r0namesszEntryPoints.namescCsdd|DS)zC Return the set of all groups of all entry points. cSsh|] }|jqSr/)rYrr/r/r0r)rwz%EntryPoints.groups..r/r-r/r/r0groups$szEntryPoints.groupscs|fdd||DS)Nc3s|]}|VqdSr2)rtrrsr/r0r@-rwz-EntryPoints._from_text_for..) _from_text)rDrErZr/rsr0_from_text_for+szEntryPoints._from_text_forcCsddt|pdDS)Ncss&|]}t|jj|jj|jdVqdS)r\N)rXr;r,)r>itemr/r/r0r@1sz)EntryPoints._from_text..rc)r:rF)rEr/r/r0r/s zEntryPoints._from_textN)r5r6r7r8 __slots__rrrr9rrrVrrWrr/r/r/r0rs  rc@sXeZdZUdZded<ded<ded<dd d d d d Zd dddZddddZdS) PackagePathz"A reference to a path in a packagezOptional[FileHash]rrsizerrZutf-8r()encodingr*cCs|j|dS)Nr)locate read_text)r.rr/r/r0r>szPackagePath.read_textbytesr)cCs |Sr2)r read_bytesr-r/r/r0 read_binaryAszPackagePath.read_binaryrcCs |j|S)z'Return a path-like object for this path)rZ locate_filer-r/r/r0rDszPackagePath.locateN)r)r5r6r7r8rrrrr/r/r/r0r7s rc@s*eZdZdddddZddddZd S) FileHashr(r[)specr*cCs|d\|_}|_dS)N=) partitionmoder;)r.r_r/r/r0r`JszFileHash.__init__r)cCsd|jd|jdS)Nz)rr;r-r/r/r0rMszFileHash.__repr__N)r5r6r7r`rr/r/r/r0rIsrcseZdZfddZZS)DeprecatedNonAbstractcsLddtD}fdd|D}|r@tjd|tddtS)NcSsh|]}t|D]}|qqSr/)r^)r>subclassr,r/r/r0rTsz0DeprecatedNonAbstract.__new__..cs"h|]}tt|ddr|qS)__isabstractmethod__Frur>r,rDr/r0rWszUnimplemented abstract methods ) stacklevel)inspectgetmrowarningswarnDeprecationWarningsuper__new__)rDr3kwargsZ all_namesZabstractrrr0rSs zDeprecatedNonAbstract.__new__)r5r6r7r __classcell__r/r/rr0rQsrc@sTeZdZdZejddddZejdddd d Zed dd d dZ edddddddZ e dddddZ e ddZ eddddZed dddZedd Zed dd!d"Zed#dd$d%Zed&dd'd(Zd)d*Zd+d,Zd-d.Zed/dd0d1Zd2d3Zd4d5Zed6d7Ze d8d9Zed:d;ZdraF An abstract Python distribution package. Custom providers may derive from this class and define the abstract methods to provide a concrete implementation for their environment. Some providers may opt to override the default implementation of some properties to bypass the file-reading mechanism. Optional[str]r)cCsdS)aAttempt to load metadata file given by the name. Python distribution metadata is organized by blobs of text typically represented as "files" in the metadata directory (e.g. package-1.0.dist-info). These files include things like: - METADATA: The distribution metadata including fields like Name and Version and Description. - entry_points.txt: A series of entry points as defined in `the entry points spec `_. - RECORD: A record of files according to `this recording spec `_. A package may provide any set of files, including those not listed here or none at all. :param filename: The name of the file in the distribution info. :return: The text if found, otherwise None. Nr/r.filenamer/r/r0rpszDistribution.read_textstr | os.PathLike[str]rpathr*cCsdS)za Given a path to a file in this distribution, return a SimplePath to it. Nr/r.rr/r/r0rszDistribution.locate_filer(rcCsB|s tdztt|j|dWSty<t|Yn0dS)aReturn the Distribution for the given package name. :param name: The name of the distribution package to search for. :return: The Distribution instance (or subclass thereof) for the named package, if found. :raises PackageNotFoundError: When the named package's distribution metadata cannot be found. :raises ValueError: When an invalid value is supplied for name. z A distribution name is required.r+N) ValueErrorrrdiscoverrr)rDr,r/r/r0 from_names  zDistribution.from_nameNcontextz$Optional[DistributionFinder.Context]Iterable[Distribution])rr*c sBr|rtdp"tjfi|tjfdd|DS)a:Return an iterable of Distribution objects for all packages. Pass a ``context`` or pass keyword arguments for constructing a context. :context: A ``DistributionFinder.Context`` object. :return: Iterable of Distribution objects for packages matching the context. z cannot accept context and kwargsc3s|]}|VqdSr2r/)r>resolverrr/r0r@sz(Distribution.discover..)rrContext itertoolschain from_iterable_discover_resolvers)rDrrr/rr0rs zDistribution.discovercCstt|S)zReturn a Distribution for the indicated metadata path. :param path: a string or path-like object :return: a concrete Distribution instance for the path )PathDistributionpathlibPathrr/r/r0atszDistribution.atcCsddtjD}td|S)z9Search the meta_path for resolvers (MetadataPathFinders).css|]}t|ddVqdS)find_distributionsNru)r>finderr/r/r0r@sz3Distribution._discover_resolvers..N)sys meta_pathrI)Zdeclaredr/r/r0rsz Distribution._discover_resolvers_meta.PackageMetadatacCs8|dp|dp|d}tt|}tt|S)avReturn the parsed metadata for this Distribution. The returned object will have keys that name the various bits of metadata per the `Core metadata specifications `_. Custom providers may provide the METADATA file or override this property. METADATAzPKG-INFOrc)rrr(rMessageemailmessage_from_string)r.Zopt_textrEr/r/r0r$s  zDistribution.metadatacCs |jdS)z8Return the 'Name' metadata for the distribution package.Namer$r-r/r/r0r,szDistribution.namecCs t|jS)z(Return a normalized version of the name.)Prepared normalizer,r-r/r/r0_normalized_nameszDistribution._normalized_namecCs |jdS)z;Return the 'Version' metadata for the distribution package.Versionrr-r/r/r0r'szDistribution.versionrcCst|d|S)z Return EntryPoints for this distribution. Custom providers may provide the ``entry_points.txt`` file or override this property. zentry_points.txt)rrrr-r/r/r0r"szDistribution.entry_pointsOptional[List[PackagePath]]csJdfdd tfdd}tdd}||pDpDS) a*Files in this distribution. :return: List of PackagePath for this distribution or None Result is `None` if the metadata file that enumerates files (i.e. RECORD for dist-info, or installed-files.txt or SOURCES.txt for egg-info) is missing. Result may be empty if the metadata exists but is empty. Custom providers are recommended to provide a "RECORD" file (in ``read_text``) or override this property to allow for callers to be able to resolve filenames provided by the package. Ncs6t|}|rt|nd|_|r&t|nd|_|_|Sr2)rrrrrrZ)r,rZsize_strresultr-r/r0 make_files z%Distribution.files..make_filecsddl}t||S)Nr)csvrreader)rOr)rr/r0 make_files sz&Distribution.files..make_filescSsttdd|S)NcSs |Sr2)rexistsrr/r/r0rwz@Distribution.files..skip_missing_files..)listrI)Z package_pathsr/r/r0skip_missing_filessz.Distribution.files..skip_missing_files)NN)r _read_files_distinfo_read_files_egginfo_installed_read_files_egginfo_sources)r.rrr/)rr.r0r#s zDistribution.filescCs|d}|o|S)z+ Read the lines of RECORD. RECORD)rrLr.rEr/r/r0rs z!Distribution._read_files_distinfocsFd}tdd|rs"dSfdd|D}tdj|S)a Read installed-files.txt and return lines in a similar CSV-parsable format as RECORD: each file must be placed relative to the site-packages directory and must also be quoted (since file names can contain literal commas). This file is written when the package is installed by pip, but it might not be written for other installation methods. Assume the file is accurate if it exists. zinstalled-files.txt_pathNc3s.|]&}|dVqdS)rcN)resolve relative_toras_posixrr.subdirr/r0r@9s  z=Distribution._read_files_egginfo_installed.."{}")rrjrLrJformat)r.rEpathsr/rr0r&s   z*Distribution._read_files_egginfo_installedcCs|d}|otdj|S)a Read SOURCES.txt and return lines in a similar CSV-parsable format as RECORD: each file name must be quoted (since it might contain literal commas). Note that SOURCES.txt is not a reliable source for what files are installed by a package. This file is generated for a source archive, and the files that are present there (e.g. setup.py) may not correctly reflect the files that are present after the package has been installed. z SOURCES.txtr)rrJrrLrr/r/r0rBs z(Distribution._read_files_egginfo_sourcesOptional[List[str]]cCs|p|}|ot|S)z6Generated requirements specified for this Distribution)_read_dist_info_reqs_read_egg_info_reqsr)r.reqsr/r/r0r&QszDistribution.requirescCs |jdS)Nz Requires-Dist)r$get_allr-r/r/r0rWsz!Distribution._read_dist_info_reqscCs|d}t|j|S)Nz requires.txt)rr _deps_from_requires_text)r.sourcer/r/r0rZs z Distribution._read_egg_info_reqscCs|t|Sr2)%_convert_egg_info_reqs_to_simple_reqsr:rB)rDr r/r/r0r^sz%Distribution._deps_from_requires_textc#sJddfdd}dd}|D]$}||j}|j|||jVq dS)a Historically, setuptools would solicit and store 'extra' requirements, including those with environment markers, in separate sections. More modern tools expect each dependency to be defined separately, with any relevant extras and environment markers attached directly to that requirement. This method converts the former to the latter. See _test_deps_from_requires_text for an example. cSs|od|dS)Nz extra == ""r/r+r/r/r0make_conditionnszJDistribution._convert_egg_info_reqs_to_simple_reqs..make_conditioncsX|pd}|d\}}}|r,|r,d|d}ttd||g}|rTdd|SdS)Nrc:(rz; z and )rrrIjoin)r?extrasepmarkersZ conditionsr r/r0 quoted_markerqs  zIDistribution._convert_egg_info_reqs_to_simple_reqs..quoted_markercSs dd|vS)z PEP 508 requires a space between the url_spec and the quoted_marker. Ref python/importlib_metadata#357.  @r/)reqr/r/r0 url_req_spaceyszIDistribution._convert_egg_info_reqs_to_simple_reqs..url_req_spaceN)r;r,)sectionsrrr?spacer/rr0r bs   z2Distribution._convert_egg_info_reqs_to_simple_reqscCs |dS)Nzdirect_url.json) _load_jsonr-r/r/r0originszDistribution.origincCsttj||dddS)NcSstjfi|Sr2)typesSimpleNamespace)datar/r/r0rrwz)Distribution._load_json..) object_hook)r jsonloadsrrr/r/r0rszDistribution._load_json)r5r6r7r8abcabstractmethodrrrVrrrWrrr9r$r,rr'r"r#rrrr&rrrr rrr/r/r/r0resP    *  " rc@s8eZdZdZGdddZejefddddZdS) rz A MetaPathFinder capable of discovering installed distributions. Custom providers should implement this interface in order to supply metadata. c@s.eZdZdZdZddZeddddZdS) zDistributionFinder.Contexta Keyword arguments presented by the caller to ``distributions()`` or ``Distribution.discover()`` to narrow the scope of a search for distributions in all DistributionFinders. Each DistributionFinder may expect any parameters and should attempt to honor the canonical parameters defined below when appropriate. This mechanism gives a custom provider a means to solicit additional details from the caller beyond "name" and "path" when searching distributions. For example, imagine a provider that exposes suites of packages in either a "public" or "private" ``realm``. A caller may wish to query only for distributions in a particular realm and could call ``distributions(realm="private")`` to signal to the custom provider to only include distributions from that realm. NcKst||dSr2r])r.rr/r/r0r`sz#DistributionFinder.Context.__init__ror)cCst|dtjS)z The sequence of directory path that a distribution finder should search. Typically refers to Python installed package paths such as "site-packages" directories and defaults to ``sys.path``. r)r^getrrr-r/r/r0rs zDistributionFinder.Context.path)r5r6r7r8r,r`r9rr/r/r/r0rs rrr)cCsdS)z Find distributions. Return an iterable of all Distribution instances capable of loading the metadata for packages matching the ``context``, a DistributionFinder.Context instance. Nr/)r.rr/r/r0rsz%DistributionFinder.find_distributionsN)r5r6r7r8rr#r$rr/r/r/r0rs+rcsheZdZdZefddZddZddZdd Z d d Z d d Z e ddZ eddZZS)FastPathaq Micro-optimized class for searching a root for children. Root is a path on the file system that may contain metadata directories either as natural directories or within a zip file. >>> FastPath('').children() ['...'] FastPath objects are cached and recycled for any given root. >>> FastPath('foobar') is FastPath('foobar') True cs t|Sr2)rr)rDrootrr/r0rszFastPath.__new__cCs ||_dSr2)r')r.r'r/r/r0r`szFastPath.__init__cCst|j|Sr2)rrr')r.childr/r/r0joinpathszFastPath.joinpathcCsltt t|jpdWdS1s.0Ytt|WdS1s^0YgSNrd)r Exceptionoslistdirr' zip_childrenr-r/r/r0childrens  . &zFastPath.childrencCs2t|j}|j}|j|_tdd|DS)Ncss |]}|tjddVqdS)rrN)rg posixpathr)r>r(r/r/r0r@rwz(FastPath.zip_children..)zipprr'namelistr)dictfromkeys)r.zip_pathrr/r/r0r.s  zFastPath.zip_childrencCs||j|Sr2)lookupmtimesearchr4r/r/r0r8szFastPath.searchcCsDttt|jjWdS1s,0Y|jdSr2)rOSErrorr,statr'st_mtimer6 cache_clearr-r/r/r0r7s ,zFastPath.mtimecCst|Sr2)Lookup)r.r7r/r/r0r6szFastPath.lookup)r5r6r7r8rh lru_cacherr`r)r/r.r8r9r7r r6rr/r/rr0r&s r&c@s,eZdZdZddddZdddd Zd S) r=zK A micro-optimized class for searching a (fast) path for metadata. r&rc Cstj|j}|d}tt|_tt|_ | D]}|}|dr| dd dd}t |}|j|||q8|r8|dkr8| dd dd}t |}|j |||q8|j|j dS)z Calculate all of the children representing metadata. From the children in the path, calculate early all of the children that appear to represent metadata (infos) or legacy metadata (eggs). z.eggz .dist-infoz .egg-infordr-zegg-infoN)r,rbasenamer'lowerrNrrinfoseggsr/ rpartitionrrrappendr)legacy_normalizefreeze) r.rbaseZ base_is_eggr(lowr, normalizedlegacy_normalizedr/r/r0r` s          zLookup.__init__rpreparedcCsP|r|j|jntj|j}|r2|j|jntj|j}t||S)zG Yield all infos and eggs matching the Prepared query. )rCrKrrrr{rDrL)r.rNrCrDr/r/r0r8&sz Lookup.searchN)r5r6r7r8r`r8r/r/r/r0r=sr=c@sFeZdZdZdZdZddddZeddZed d Z d d Z dS) ra A prepared search query for metadata on a possibly-named package. Pre-calculates the normalization to prevent repeated operations. >>> none = Prepared(None) >>> none.normalized >>> none.legacy_normalized >>> bool(none) False >>> sample = Prepared('Sample__Pkg-name.foo') >>> sample.normalized 'sample_pkg_name_foo' >>> sample.legacy_normalized 'sample__pkg_name.foo' >>> bool(sample) True Nrr+cCs.||_|durdS|||_|||_dSr2)r,rrKrGrLr4r/r/r0r`Ns  zPrepared.__init__cCstdd|ddS)zC PEP 503 normalization plus dashes as underscores. z[-_.]+r@r)rqsubrBreplacer+r/r/r0rUszPrepared.normalizecCs|ddS)z| Normalize the package name as found in the convention in older packaging tools versions and specs. r@r)rBrPr+r/r/r0rG\szPrepared.legacy_normalizecCs t|jSr2)boolr,r-r/r/r0__bool__dszPrepared.__bool__) r5r6r7r8rKrLr`rWrrGrRr/r/r/r0r7s  rc@sHeZdZdZeefddddZeddZeddd d Z d S) MetadataPathFinderzA degenerate finder for distribution packages on the file system. This finder supplies only a find_distributions() method for versions of Python that do not have a PathFinder find_distributions(). zIterable[PathDistribution]r)cCs||j|j}tt|S)a  Find distributions. Return an iterable of all Distribution instances capable of loading the metadata for packages matching ``context.name`` (or all names if ``None`` indicated) along the paths in the list of directories ``context.path``. ) _search_pathsr,rrJr)rDrfoundr/r/r0rps z%MetadataPathFinder.find_distributionscs(t|tjfddtt|DS)z1Find metadata directories in paths heuristically.c3s|]}|VqdSr2)r8)r>rrMr/r0r@sz3MetadataPathFinder._search_paths..)rrrrrJr&)rDr,rr/rMr0rTsz MetadataPathFinder._search_pathsr[cCstjdSr2)r&rr<rr/r/r0invalidate_cachessz$MetadataPathFinder.invalidate_cachesN) r5r6r7r8rVrrrrTrVr/r/r/r0rShs rScsfeZdZdddddZdddd d Zejje_dddd d Zefd dZ e ddZ Z S)rrr[rcCs ||_dS)zfConstruct a distribution. :param path: SimplePath indicating the metadata directory. N)rrr/r/r0r`szPathDistribution.__init__rr)rr*cCsHtttttt$|j|jddWdS1s:0YdS)Nrr) rFileNotFoundErrorIsADirectoryErrorrNotADirectoryErrorPermissionErrorrr)rrr/r/r0rs2zPathDistribution.read_textcCs |jj|Sr2)rparentrr/r/r0rszPathDistribution.locate_filecs.tjt|j}ttj||p,t j S)zz Performance optimization: where possible, resolve the normalized name from the file system path. ) r,rrAr(rr rr_name_from_stemrr)r.stemrr/r0rsz!PathDistribution._normalized_namecCs0tj|\}}|dvrdS|d\}}}|S)a7 >>> PathDistribution._name_from_stem('foo-3.0.egg-info') 'foo' >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info') 'CherryPy' >>> PathDistribution._name_from_stem('face.egg-info') 'face' >>> PathDistribution._name_from_stem('foo.bar') r?Nr@)r,rsplitextr)r]rextr,rrestr/r/r0r\s z PathDistribution._name_from_stem) r5r6r7r`rrr8rr9rrWr\rr/r/rr0rs  rr()distribution_namer*cCs t|S)zGet the ``Distribution`` instance for the named package. :param distribution_name: The name of the distribution package as a string. :return: A ``Distribution`` instance (or subclass thereof). )rrrar/r/r0r sr rr)cKstjfi|S)z|Get all ``Distribution`` instances in the current environment. :return: An iterable of ``Distribution`` instances. )rr)rr/r/r0r!sr!rcCs t|jS)zGet the metadata for the named package. :param distribution_name: The name of the distribution package to query. :return: A PackageMetadata containing the parsed metadata. )rrr$rbr/r/r0r$sr$cCs t|jS)zGet the version string for the named package. :param distribution_name: The name of the distribution package to query. :return: The version string for the package as defined in the package's "Version" metadata key. )r r'rbr/r/r0r'sr')keycKs0tjddttD}t|jfi|S)aReturn EntryPoint objects for all installed packages. Pass selection parameters (group or name) to filter the result to entry points matching those properties (see EntryPoints.select()). :return: EntryPoints for all installed packages. css|] }|jVqdSr2)r")r>rZr/r/r0r@szentry_points..)rrr_uniquer!rr)r|Zepsr/r/r0r"s r"rcCs t|jS)zReturn a list of files for the named package. :param distribution_name: The name of the distribution package to query. :return: List of files composing the distribution. )r r#rbr/r/r0r#sr#rcCs t|jS)z Return a list of requirements for the named package. :return: An iterable of requirements, suitable for packaging.requirement.Requirement. )r r&rbr/r/r0r&sr&zMapping[str, List[str]]cCsHtt}tD].}t|p"t|D]}|||jdq$qt|S)z Return a mapping of top-level packages to their distributions. >>> import collections.abc >>> pkgs = packages_distributions() >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) True r) collections defaultdictrr!_top_level_declared_top_level_inferredrFr$r3)Z pkg_to_distrZpkgr/r/r0r%s  r%cCs|dp dS)Nz top_level.txtrc)rrgrsr/r/r0rg#srgrrcCs|j^}}|r|SdS)zB Return the top-most parent as long as there is a parent. N)parts)r,topr`r/r/r0_topmost's rlcCst|pt|pt|S)a Infer a possibly importable module name from a name presumed on sys.path. >>> _get_toplevel_name(PackagePath('foo.py')) 'foo' >>> _get_toplevel_name(PackagePath('foo')) 'foo' >>> _get_toplevel_name(PackagePath('foo.pyc')) 'foo' >>> _get_toplevel_name(PackagePath('foo/__init__.py')) 'foo' >>> _get_toplevel_name(PackagePath('foo.pth')) 'foo.pth' >>> _get_toplevel_name(PackagePath('foo.dist-info')) 'foo.dist-info' )rlr getmodulenamer(r+r/r/r0_get_toplevel_name/s rncCs&tttt|j}dd}t||S)NcSsd|vSr*r/r+r/r/r0importable_nameKsz,_top_level_inferred..importable_name)setrJrnr r#rI)rZ opt_namesror/r/r0rhHsrh)W __future__rr,rqr#rr!r1rrrrryrSrrhrr0rercrrcompatr _collectionsrrZ_compatr r _functoolsr r _itertoolsr rrr contextlibr importlibr importlib.abcrrtypingrrrrrrrr__all__ModuleNotFoundErrorrr:rXrr PurePosixPathrrrrrr&r=rrSrr r!r$r'partialZnormalized_namerdr"r#r&r%rgrlrnrhr/r/r/r0s~      ( A|8->631#7      __pycache__/_adapters.cpython-39.pyc000064400000005565151061232500013355 0ustar00a \f @spddlZddlZddlZddlZddlZddlmZddlm Z ej ej de e ddZ Gdd d ejjZdS) N) FoldedCase) pypy_partialzFImplicit None on return values is deprecated and will raise KeyErrors.) stacklevelcsleZdZeeegdZejj dfdd Z ddZ fddZ fd d Z d d Zed dZZS)Message) ClassifierzObsoletes-DistPlatformz Project-URLz Provides-DistzProvides-Extraz Requires-DistzRequires-ExternalzSupported-PlatformZDynamic)origcs"t|}t|t||SN)super__new__varsupdate)clsr res __class__F/usr/local/lib/python3.9/site-packages/importlib_metadata/_adapters.pyr *s zMessage.__new__cOs||_dSr )_repair_headers_headers)selfargskwargsrrr__init__/szMessage.__init__cs tSr )r __iter__rrrrr3szMessage.__iter__cst|}|durt|S)z Warn users that a ``KeyError`` can be expected when a missing key is supplied. Ref python/importlib_metadata#371. N)r __getitem___warn)ritemrrrrr6s zMessage.__getitem__cs>ddfddt|dD}|jr:|d|f|S)NcSs|r d|vr|Std|S)zCorrect for RFC822 indentation z )textwrapdedent)valuerrrredentAs z'Message._repair_headers..redentcsg|]\}}||fqSrr).0keyr$r%rr Gz+Message._repair_headers..r Description)r_payloadappend get_payload)rheadersrr(rr@s zMessage._repair_headerscs fdd}tt|ttS)z[ Convert PackageMetadata to a JSON-compatible format per PEP 0566. csH|jvr|n|}|dkr0td|}|dd}||fS)NZKeywordsz\s+-_)multiple_use_keysget_allresplitlowerreplace)r'r$Ztkrrr transformSs  zMessage.json..transform)dictmapr)rr8rrrjsonLs z Message.json)__name__ __module__ __qualname__setr:rr2emailmessagerr rrrrpropertyr; __classcell__rrrrrs   r) functoolswarningsr4r" email.messager@Z_textrZ_compatrpartialwarnDeprecationWarningrrArrrrrs  __pycache__/_collections.cpython-39.pyc000064400000002772151061232500014065 0ustar00a \f@s6ddlZGdddejZGdddeddZdS)Ncs(eZdZdZfddZddZZS)FreezableDefaultDicta! Often it is desirable to prevent the mutation of a default dict after its initial construction, such as to prevent mutation during iteration. >>> dd = FreezableDefaultDict(list) >>> dd[0].append('1') >>> dd.freeze() >>> dd[1] [] >>> len(dd) 1 cst|dtj|S)N_frozen)getattrsuper __missing__)selfkey __class__I/usr/local/lib/python3.9/site-packages/importlib_metadata/_collections.pyrsz FreezableDefaultDict.__missing__csfdd_dS)NcsS)N)default_factory)rrr r z-FreezableDefaultDict.freeze..)rrr rr freezeszFreezableDefaultDict.freeze)__name__ __module__ __qualname____doc__rr __classcell__r r r r rs rc@seZdZeddZdS)PaircCs|ttj|ddS)N=)mapstrstripsplit)clstextr r r parsesz Pair.parseN)rrr classmethodr r r r r rsrz name value) collections defaultdictr namedtuplerr r r r s__pycache__/_compat.cpython-39.pyc000064400000003510151061232500013021 0ustar00a \f"@sBddlZddlZddgZddZddZGdddZdd ZdS) Ninstall NullFindercCstj|t|S)z Class decorator for installation on sys.meta_path. Adds the backport DistributionFinder to sys.meta_path and attempts to disable the finder functionality of the stdlib DistributionFinder. )sys meta_pathappenddisable_stdlib_finder)clsr D/usr/local/lib/python3.9/site-packages/importlib_metadata/_compat.pyrscCs"dd}t|tjD]}|`qdS)z Give the backport primacy for discovering path-based distributions by monkey-patching the stdlib O_O. See #91 for more background for rationale on this sketchy behavior. cSst|dddkot|dS)N __module___frozen_importlib_externalfind_distributions)getattrhasattr)finderr r r matchessz&disable_stdlib_finder..matchesN)filterrrr )rrr r r rs rc@seZdZdZeddZdS)rzi A "Finder" (aka "MetaPathFinder") that never finds any modules, but may find distributions. cOsdS)Nr )argskwargsr r r find_spec-szNullFinder.find_specN)__name__r __qualname____doc__ staticmethodrr r r r r'scCstdk}||S)zY Adjust for variable stacklevel on partial under PyPy. Workaround for #327. PyPy)platformpython_implementation)valZis_pypyr r r pypy_partial2s r)rr__all__rrrrr r r r s   __pycache__/_functools.cpython-39.pyc000064400000006060151061232500013555 0ustar00a \fO @s&ddlZddlZdddZddZdS)Ncs(p tfdd}dd|_|S)aV Wrap lru_cache to support storing the cache data in the object instances. Abstracts the common paradigm where the method explicitly saves an underscore-prefixed protected property on first call and returns that subsequently. >>> class MyClass: ... calls = 0 ... ... @method_cache ... def method(self, value): ... self.calls += 1 ... return value >>> a = MyClass() >>> a.method(3) 3 >>> for x in range(75): ... res = a.method(x) >>> a.calls 75 Note that the apparent behavior will be exactly like that of lru_cache except that the cache is stored on each instance, so values in one instance will not flush values from another, and when an instance is deleted, so are the cached values for that instance. >>> b = MyClass() >>> for x in range(35): ... res = b.method(x) >>> b.calls 35 >>> a.method(0) 0 >>> a.calls 75 Note that if method had been decorated with ``functools.lru_cache()``, a.calls would have been 76 (due to the cached value of 0 having been flushed by the 'b' instance). Clear the cache with ``.cache_clear()`` >>> a.method.cache_clear() Same for a method that hasn't yet been called. >>> c = MyClass() >>> c.method.cache_clear() Another cache wrapper may be supplied: >>> cache = functools.lru_cache(maxsize=2) >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) >>> a = MyClass() >>> a.method2() 3 Caution - do not subsequently wrap the method with another decorator, such as ``@property``, which changes the semantics of the function. See also http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ for another implementation and additional justification. cs0t|}|}t|j|||i|SN)types MethodTypesetattr__name__)selfargskwargsZ bound_methodZ cached_method cache_wrappermethodG/usr/local/lib/python3.9/site-packages/importlib_metadata/_functools.pywrapperKs zmethod_cache..wrappercSsdSrr r r r rSzmethod_cache..) functools lru_cache cache_clear)r r rr r r method_cachesC  rcstfdd}|S)z Wrap func so it's not called if its first param is None >>> print_text = pass_none(print) >>> print_text('text') text >>> print_text(None) cs"|dur|g|Ri|SdSrr )paramrr funcr rrcszpass_none..wrapper)rwraps)rrr rr pass_noneYs r)N)rrrrr r r rs S__pycache__/_itertools.cpython-39.pyc000064400000003715151061232500013571 0ustar00a \f@s*ddlmZdddZeeffddZdS)) filterfalseNccsbt}|j}|dur6t|j|D]}|||Vq n(|D]"}||}||vr:|||Vq:dS)zHList unique elements, preserving order. Remember all elements ever seen.N)setaddr __contains__)iterablekeyseenZseen_addelementkr G/usr/local/lib/python3.9/site-packages/importlib_metadata/_itertools.pyunique_everseens r cCsX|durtdS|dur,t||r,t|fSz t|WStyRt|fYS0dS)axIf *obj* is iterable, return an iterator over its items:: >>> obj = (1, 2, 3) >>> list(always_iterable(obj)) [1, 2, 3] If *obj* is not iterable, return a one-item iterable containing *obj*:: >>> obj = 1 >>> list(always_iterable(obj)) [1] If *obj* is ``None``, return an empty iterable: >>> obj = None >>> list(always_iterable(None)) [] By default, binary and text strings are not considered iterable:: >>> obj = 'foo' >>> list(always_iterable(obj)) ['foo'] If *base_type* is set, objects for which ``isinstance(obj, base_type)`` returns ``True`` won't be considered iterable. >>> obj = {'a': 1} >>> list(always_iterable(obj)) # Iterate over the dict's keys ['a'] >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit [{'a': 1}] Set *base_type* to ``None`` to avoid any special handling and treat objects Python considers iterable as iterable: >>> obj = 'foo' >>> list(always_iterable(obj, base_type=None)) ['f', 'o', 'o'] Nr )iter isinstance TypeError)objZ base_typer r r always_iterables)   r)N) itertoolsrr strbytesrr r r r s  __pycache__/_meta.cpython-39.pyc000064400000006224151061232500012471 0ustar00a \f @stddlmZddlZddlmZddlmZmZmZmZm Z m Z m Z m Z e dZ GdddeZGdd d eZdS) ) annotationsN)Protocol)AnyDictIteratorListOptionalTypeVarUnionoverload_Tc@seZdZddddZddddd Zddd d d Zd dddZed!ddddddZeddddddZed"ddddddZ eddddddZ e dddd Z dS)#PackageMetadataintreturncCsdSNselfrrB/usr/local/lib/python3.9/site-packages/importlib_metadata/_meta.py__len__ zPackageMetadata.__len__strbool)itemrcCsdSrr)rrrrr __contains__rzPackageMetadata.__contains__)keyrcCsdSrr)rrrrr __getitem__rzPackageMetadata.__getitem__z Iterator[str]cCsdSrrrrrr__iter__rzPackageMetadata.__iter__NNonez Optional[str])namefailobjrcCsdSrrrr r!rrrgetszPackageMetadata.getr zUnion[str, _T]cCsdSrrr"rrrr#szOptional[List[Any]]cCsdSrrr"rrrget_allszPackageMetadata.get_allzUnion[List[Any], _T]cCsdS)zP Return all values associated with a possibly multi-valued key. Nrr"rrrr$"sz Dict[str, Union[str, List[str]]]cCsdS)z9 A JSON-compatible form of the metadata. Nrrrrrjson(szPackageMetadata.json)N)N) __name__ __module__ __qualname__rrrrr r#r$propertyr%rrrrr s r c@sneZdZdZdddddZdddddZeddd d Zdd dd dZddddZ ddddZ d S) SimplePathzD A minimal subset of pathlib.Path required by Distribution. zUnion[str, os.PathLike[str]])otherrcCsdSrrrr+rrrjoinpath4szSimplePath.joinpathcCsdSrrr,rrr __truediv__8szSimplePath.__truediv__rcCsdSrrrrrrparent<szSimplePath.parentNrcCsdSrr)rencodingrrr read_text?rzSimplePath.read_textbytescCsdSrrrrrr read_bytesArzSimplePath.read_bytesrcCsdSrrrrrrexistsCrzSimplePath.exists)N) r&r'r(__doc__r-r.r)r/r1r3r4rrrrr*/sr*) __future__rostypingrrrrrrr r r r r r*rrrrs  ($__pycache__/_text.cpython-39.pyc000064400000005767151061232500012542 0ustar00a \fv@s(ddlZddlmZGdddeZdS)N) method_cachecsreZdZdZddZddZddZdd Zd d Zfd d Z ddZ e fddZ ddZ dddZZS) FoldedCasea{ A case insensitive string class; behaves just like str except compares equal when the only variation is case. >>> s = FoldedCase('hello world') >>> s == 'Hello World' True >>> 'Hello World' == s True >>> s != 'Hello World' False >>> s.index('O') 4 >>> s.split('O') ['hell', ' w', 'rld'] >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) ['alpha', 'Beta', 'GAMMA'] Sequence membership is straightforward. >>> "Hello World" in [s] True >>> s in ["Hello World"] True You may test for set inclusion, but candidate and elements must both be folded. >>> FoldedCase("Hello World") in {s} True >>> s in {FoldedCase("Hello World")} True String inclusion works as long as the FoldedCase object is on the right. >>> "hello" in FoldedCase("Hello World") True But not if the FoldedCase object is on the left: >>> FoldedCase('hello') in 'Hello World' False In that case, use in_: >>> FoldedCase('hello').in_('Hello World') True >>> FoldedCase('hello') > FoldedCase('Hello') False cCs||kSNlowerselfotherr B/usr/local/lib/python3.9/site-packages/importlib_metadata/_text.py__lt__CszFoldedCase.__lt__cCs||kSrrrr r r __gt__FszFoldedCase.__gt__cCs||kSrrrr r r __eq__IszFoldedCase.__eq__cCs||kSrrrr r r __ne__LszFoldedCase.__ne__cCs t|Sr)hashrr r r r __hash__OszFoldedCase.__hash__cst|Sr)superr __contains__r __class__r r rRszFoldedCase.__contains__cCs |t|vS)zDoes self appear in other?)rrr r r in_UszFoldedCase.in_cs tSr)rrrrr r rZszFoldedCase.lowercCs||Sr)rindex)r subr r r r^szFoldedCase.index rcCs tt|tj}|||Sr)recompileescapeIsplit)r Zsplittermaxsplitpatternr r r r aszFoldedCase.split)rr)__name__ __module__ __qualname____doc__r rrrrrrrrrr __classcell__r r rr rs; r)r _functoolsrstrrr r r r s __pycache__/diagnose.cpython-39.pyc000064400000001456151061232500013177 0ustar00a \f{@s6ddlZddlmZddZddZedkr2edS) N) DistributioncCsTtd|ttj|gd}|s$dStdt|dddtddd |DdS) NZ InspectingpathFoundz packages: )endz, css|] }|jVqdSN)name).0distr E/usr/local/lib/python3.9/site-packages/importlib_metadata/diagnose.py zinspect..)printlistrZdiscoverlenjoin)rdistsr r rinspects  rcCstjD] }t|qdSr )sysrrrr r rruns r__main__)rrrr__name__r r r rs   __init__.py000064400000102776151061232500006665 0ustar00from __future__ import annotations import os import re import abc import sys import json import zipp import email import types import inspect import pathlib import operator import textwrap import warnings import functools import itertools import posixpath import collections from . import _adapters, _meta from .compat import py39 from ._collections import FreezableDefaultDict, Pair from ._compat import ( NullFinder, install, ) from ._functools import method_cache, pass_none from ._itertools import always_iterable, unique_everseen from ._meta import PackageMetadata, SimplePath from contextlib import suppress from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast __all__ = [ 'Distribution', 'DistributionFinder', 'PackageMetadata', 'PackageNotFoundError', 'distribution', 'distributions', 'entry_points', 'files', 'metadata', 'packages_distributions', 'requires', 'version', ] class PackageNotFoundError(ModuleNotFoundError): """The package was not found.""" def __str__(self) -> str: return f"No package metadata was found for {self.name}" @property def name(self) -> str: # type: ignore[override] (name,) = self.args return name class Sectioned: """ A simple entry point config parser for performance >>> for item in Sectioned.read(Sectioned._sample): ... print(item) Pair(name='sec1', value='# comments ignored') Pair(name='sec1', value='a = 1') Pair(name='sec1', value='b = 2') Pair(name='sec2', value='a = 2') >>> res = Sectioned.section_pairs(Sectioned._sample) >>> item = next(res) >>> item.name 'sec1' >>> item.value Pair(name='a', value='1') >>> item = next(res) >>> item.value Pair(name='b', value='2') >>> item = next(res) >>> item.name 'sec2' >>> item.value Pair(name='a', value='2') >>> list(res) [] """ _sample = textwrap.dedent( """ [sec1] # comments ignored a = 1 b = 2 [sec2] a = 2 """ ).lstrip() @classmethod def section_pairs(cls, text): return ( section._replace(value=Pair.parse(section.value)) for section in cls.read(text, filter_=cls.valid) if section.name is not None ) @staticmethod def read(text, filter_=None): lines = filter(filter_, map(str.strip, text.splitlines())) name = None for value in lines: section_match = value.startswith('[') and value.endswith(']') if section_match: name = value.strip('[]') continue yield Pair(name, value) @staticmethod def valid(line: str): return line and not line.startswith('#') class EntryPoint: """An entry point as defined by Python packaging conventions. See `the packaging docs on entry points `_ for more information. >>> ep = EntryPoint( ... name=None, group=None, value='package.module:attr [extra1, extra2]') >>> ep.module 'package.module' >>> ep.attr 'attr' >>> ep.extras ['extra1', 'extra2'] """ pattern = re.compile( r'(?P[\w.]+)\s*' r'(:\s*(?P[\w.]+)\s*)?' r'((?P\[.*\])\s*)?$' ) """ A regular expression describing the syntax for an entry point, which might look like: - module - package.module - package.module:attribute - package.module:object.attribute - package.module:attr [extra1, extra2] Other combinations are possible as well. The expression is lenient about whitespace around the ':', following the attr, and following any extras. """ name: str value: str group: str dist: Optional[Distribution] = None def __init__(self, name: str, value: str, group: str) -> None: vars(self).update(name=name, value=value, group=group) def load(self) -> Any: """Load the entry point from its definition. If only a module is indicated by the value, return that module. Otherwise, return the named object. """ match = cast(Match, self.pattern.match(self.value)) module = import_module(match.group('module')) attrs = filter(None, (match.group('attr') or '').split('.')) return functools.reduce(getattr, attrs, module) @property def module(self) -> str: match = self.pattern.match(self.value) assert match is not None return match.group('module') @property def attr(self) -> str: match = self.pattern.match(self.value) assert match is not None return match.group('attr') @property def extras(self) -> List[str]: match = self.pattern.match(self.value) assert match is not None return re.findall(r'\w+', match.group('extras') or '') def _for(self, dist): vars(self).update(dist=dist) return self def matches(self, **params): """ EntryPoint matches the given parameters. >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') >>> ep.matches(group='foo') True >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') True >>> ep.matches(group='foo', name='other') False >>> ep.matches() True >>> ep.matches(extras=['extra1', 'extra2']) True >>> ep.matches(module='bing') True >>> ep.matches(attr='bong') True """ attrs = (getattr(self, param) for param in params) return all(map(operator.eq, params.values(), attrs)) def _key(self): return self.name, self.value, self.group def __lt__(self, other): return self._key() < other._key() def __eq__(self, other): return self._key() == other._key() def __setattr__(self, name, value): raise AttributeError("EntryPoint objects are immutable.") def __repr__(self): return ( f'EntryPoint(name={self.name!r}, value={self.value!r}, ' f'group={self.group!r})' ) def __hash__(self) -> int: return hash(self._key()) class EntryPoints(tuple): """ An immutable collection of selectable EntryPoint objects. """ __slots__ = () def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] """ Get the EntryPoint in self matching name. """ try: return next(iter(self.select(name=name))) except StopIteration: raise KeyError(name) def __repr__(self): """ Repr with classname and tuple constructor to signal that we deviate from regular tuple behavior. """ return '%s(%r)' % (self.__class__.__name__, tuple(self)) def select(self, **params) -> EntryPoints: """ Select entry points from self that match the given parameters (typically group and/or name). """ return EntryPoints(ep for ep in self if py39.ep_matches(ep, **params)) @property def names(self) -> Set[str]: """ Return the set of all names of all entry points. """ return {ep.name for ep in self} @property def groups(self) -> Set[str]: """ Return the set of all groups of all entry points. """ return {ep.group for ep in self} @classmethod def _from_text_for(cls, text, dist): return cls(ep._for(dist) for ep in cls._from_text(text)) @staticmethod def _from_text(text): return ( EntryPoint(name=item.value.name, value=item.value.value, group=item.name) for item in Sectioned.section_pairs(text or '') ) class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" hash: Optional[FileHash] size: int dist: Distribution def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override] return self.locate().read_text(encoding=encoding) def read_binary(self) -> bytes: return self.locate().read_bytes() def locate(self) -> SimplePath: """Return a path-like object for this path""" return self.dist.locate_file(self) class FileHash: def __init__(self, spec: str) -> None: self.mode, _, self.value = spec.partition('=') def __repr__(self) -> str: return f'' class DeprecatedNonAbstract: # Required until Python 3.14 def __new__(cls, *args, **kwargs): all_names = { name for subclass in inspect.getmro(cls) for name in vars(subclass) } abstract = { name for name in all_names if getattr(getattr(cls, name), '__isabstractmethod__', False) } if abstract: warnings.warn( f"Unimplemented abstract methods {abstract}", DeprecationWarning, stacklevel=2, ) return super().__new__(cls) class Distribution(DeprecatedNonAbstract): """ An abstract Python distribution package. Custom providers may derive from this class and define the abstract methods to provide a concrete implementation for their environment. Some providers may opt to override the default implementation of some properties to bypass the file-reading mechanism. """ @abc.abstractmethod def read_text(self, filename) -> Optional[str]: """Attempt to load metadata file given by the name. Python distribution metadata is organized by blobs of text typically represented as "files" in the metadata directory (e.g. package-1.0.dist-info). These files include things like: - METADATA: The distribution metadata including fields like Name and Version and Description. - entry_points.txt: A series of entry points as defined in `the entry points spec `_. - RECORD: A record of files according to `this recording spec `_. A package may provide any set of files, including those not listed here or none at all. :param filename: The name of the file in the distribution info. :return: The text if found, otherwise None. """ @abc.abstractmethod def locate_file(self, path: str | os.PathLike[str]) -> SimplePath: """ Given a path to a file in this distribution, return a SimplePath to it. """ @classmethod def from_name(cls, name: str) -> Distribution: """Return the Distribution for the given package name. :param name: The name of the distribution package to search for. :return: The Distribution instance (or subclass thereof) for the named package, if found. :raises PackageNotFoundError: When the named package's distribution metadata cannot be found. :raises ValueError: When an invalid value is supplied for name. """ if not name: raise ValueError("A distribution name is required.") try: return next(iter(cls.discover(name=name))) except StopIteration: raise PackageNotFoundError(name) @classmethod def discover( cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs ) -> Iterable[Distribution]: """Return an iterable of Distribution objects for all packages. Pass a ``context`` or pass keyword arguments for constructing a context. :context: A ``DistributionFinder.Context`` object. :return: Iterable of Distribution objects for packages matching the context. """ if context and kwargs: raise ValueError("cannot accept context and kwargs") context = context or DistributionFinder.Context(**kwargs) return itertools.chain.from_iterable( resolver(context) for resolver in cls._discover_resolvers() ) @staticmethod def at(path: str | os.PathLike[str]) -> Distribution: """Return a Distribution for the indicated metadata path. :param path: a string or path-like object :return: a concrete Distribution instance for the path """ return PathDistribution(pathlib.Path(path)) @staticmethod def _discover_resolvers(): """Search the meta_path for resolvers (MetadataPathFinders).""" declared = ( getattr(finder, 'find_distributions', None) for finder in sys.meta_path ) return filter(None, declared) @property def metadata(self) -> _meta.PackageMetadata: """Return the parsed metadata for this Distribution. The returned object will have keys that name the various bits of metadata per the `Core metadata specifications `_. Custom providers may provide the METADATA file or override this property. """ opt_text = ( self.read_text('METADATA') or self.read_text('PKG-INFO') # This last clause is here to support old egg-info files. Its # effect is to just end up using the PathDistribution's self._path # (which points to the egg-info file) attribute unchanged. or self.read_text('') ) text = cast(str, opt_text) return _adapters.Message(email.message_from_string(text)) @property def name(self) -> str: """Return the 'Name' metadata for the distribution package.""" return self.metadata['Name'] @property def _normalized_name(self): """Return a normalized version of the name.""" return Prepared.normalize(self.name) @property def version(self) -> str: """Return the 'Version' metadata for the distribution package.""" return self.metadata['Version'] @property def entry_points(self) -> EntryPoints: """ Return EntryPoints for this distribution. Custom providers may provide the ``entry_points.txt`` file or override this property. """ return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) @property def files(self) -> Optional[List[PackagePath]]: """Files in this distribution. :return: List of PackagePath for this distribution or None Result is `None` if the metadata file that enumerates files (i.e. RECORD for dist-info, or installed-files.txt or SOURCES.txt for egg-info) is missing. Result may be empty if the metadata exists but is empty. Custom providers are recommended to provide a "RECORD" file (in ``read_text``) or override this property to allow for callers to be able to resolve filenames provided by the package. """ def make_file(name, hash=None, size_str=None): result = PackagePath(name) result.hash = FileHash(hash) if hash else None result.size = int(size_str) if size_str else None result.dist = self return result @pass_none def make_files(lines): # Delay csv import, since Distribution.files is not as widely used # as other parts of importlib.metadata import csv return starmap(make_file, csv.reader(lines)) @pass_none def skip_missing_files(package_paths): return list(filter(lambda path: path.locate().exists(), package_paths)) return skip_missing_files( make_files( self._read_files_distinfo() or self._read_files_egginfo_installed() or self._read_files_egginfo_sources() ) ) def _read_files_distinfo(self): """ Read the lines of RECORD. """ text = self.read_text('RECORD') return text and text.splitlines() def _read_files_egginfo_installed(self): """ Read installed-files.txt and return lines in a similar CSV-parsable format as RECORD: each file must be placed relative to the site-packages directory and must also be quoted (since file names can contain literal commas). This file is written when the package is installed by pip, but it might not be written for other installation methods. Assume the file is accurate if it exists. """ text = self.read_text('installed-files.txt') # Prepend the .egg-info/ subdir to the lines in this file. # But this subdir is only available from PathDistribution's # self._path. subdir = getattr(self, '_path', None) if not text or not subdir: return paths = ( (subdir / name) .resolve() .relative_to(self.locate_file('').resolve()) .as_posix() for name in text.splitlines() ) return map('"{}"'.format, paths) def _read_files_egginfo_sources(self): """ Read SOURCES.txt and return lines in a similar CSV-parsable format as RECORD: each file name must be quoted (since it might contain literal commas). Note that SOURCES.txt is not a reliable source for what files are installed by a package. This file is generated for a source archive, and the files that are present there (e.g. setup.py) may not correctly reflect the files that are present after the package has been installed. """ text = self.read_text('SOURCES.txt') return text and map('"{}"'.format, text.splitlines()) @property def requires(self) -> Optional[List[str]]: """Generated requirements specified for this Distribution""" reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() return reqs and list(reqs) def _read_dist_info_reqs(self): return self.metadata.get_all('Requires-Dist') def _read_egg_info_reqs(self): source = self.read_text('requires.txt') return pass_none(self._deps_from_requires_text)(source) @classmethod def _deps_from_requires_text(cls, source): return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source)) @staticmethod def _convert_egg_info_reqs_to_simple_reqs(sections): """ Historically, setuptools would solicit and store 'extra' requirements, including those with environment markers, in separate sections. More modern tools expect each dependency to be defined separately, with any relevant extras and environment markers attached directly to that requirement. This method converts the former to the latter. See _test_deps_from_requires_text for an example. """ def make_condition(name): return name and f'extra == "{name}"' def quoted_marker(section): section = section or '' extra, sep, markers = section.partition(':') if extra and markers: markers = f'({markers})' conditions = list(filter(None, [markers, make_condition(extra)])) return '; ' + ' and '.join(conditions) if conditions else '' def url_req_space(req): """ PEP 508 requires a space between the url_spec and the quoted_marker. Ref python/importlib_metadata#357. """ # '@' is uniquely indicative of a url_req. return ' ' * ('@' in req) for section in sections: space = url_req_space(section.value) yield section.value + space + quoted_marker(section.name) @property def origin(self): return self._load_json('direct_url.json') def _load_json(self, filename): return pass_none(json.loads)( self.read_text(filename), object_hook=lambda data: types.SimpleNamespace(**data), ) class DistributionFinder(MetaPathFinder): """ A MetaPathFinder capable of discovering installed distributions. Custom providers should implement this interface in order to supply metadata. """ class Context: """ Keyword arguments presented by the caller to ``distributions()`` or ``Distribution.discover()`` to narrow the scope of a search for distributions in all DistributionFinders. Each DistributionFinder may expect any parameters and should attempt to honor the canonical parameters defined below when appropriate. This mechanism gives a custom provider a means to solicit additional details from the caller beyond "name" and "path" when searching distributions. For example, imagine a provider that exposes suites of packages in either a "public" or "private" ``realm``. A caller may wish to query only for distributions in a particular realm and could call ``distributions(realm="private")`` to signal to the custom provider to only include distributions from that realm. """ name = None """ Specific name for which a distribution finder should match. A name of ``None`` matches all distributions. """ def __init__(self, **kwargs): vars(self).update(kwargs) @property def path(self) -> List[str]: """ The sequence of directory path that a distribution finder should search. Typically refers to Python installed package paths such as "site-packages" directories and defaults to ``sys.path``. """ return vars(self).get('path', sys.path) @abc.abstractmethod def find_distributions(self, context=Context()) -> Iterable[Distribution]: """ Find distributions. Return an iterable of all Distribution instances capable of loading the metadata for packages matching the ``context``, a DistributionFinder.Context instance. """ class FastPath: """ Micro-optimized class for searching a root for children. Root is a path on the file system that may contain metadata directories either as natural directories or within a zip file. >>> FastPath('').children() ['...'] FastPath objects are cached and recycled for any given root. >>> FastPath('foobar') is FastPath('foobar') True """ @functools.lru_cache() # type: ignore def __new__(cls, root): return super().__new__(cls) def __init__(self, root): self.root = root def joinpath(self, child): return pathlib.Path(self.root, child) def children(self): with suppress(Exception): return os.listdir(self.root or '.') with suppress(Exception): return self.zip_children() return [] def zip_children(self): zip_path = zipp.Path(self.root) names = zip_path.root.namelist() self.joinpath = zip_path.joinpath return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) def search(self, name): return self.lookup(self.mtime).search(name) @property def mtime(self): with suppress(OSError): return os.stat(self.root).st_mtime self.lookup.cache_clear() @method_cache def lookup(self, mtime): return Lookup(self) class Lookup: """ A micro-optimized class for searching a (fast) path for metadata. """ def __init__(self, path: FastPath): """ Calculate all of the children representing metadata. From the children in the path, calculate early all of the children that appear to represent metadata (infos) or legacy metadata (eggs). """ base = os.path.basename(path.root).lower() base_is_egg = base.endswith(".egg") self.infos = FreezableDefaultDict(list) self.eggs = FreezableDefaultDict(list) for child in path.children(): low = child.lower() if low.endswith((".dist-info", ".egg-info")): # rpartition is faster than splitext and suitable for this purpose. name = low.rpartition(".")[0].partition("-")[0] normalized = Prepared.normalize(name) self.infos[normalized].append(path.joinpath(child)) elif base_is_egg and low == "egg-info": name = base.rpartition(".")[0].partition("-")[0] legacy_normalized = Prepared.legacy_normalize(name) self.eggs[legacy_normalized].append(path.joinpath(child)) self.infos.freeze() self.eggs.freeze() def search(self, prepared: Prepared): """ Yield all infos and eggs matching the Prepared query. """ infos = ( self.infos[prepared.normalized] if prepared else itertools.chain.from_iterable(self.infos.values()) ) eggs = ( self.eggs[prepared.legacy_normalized] if prepared else itertools.chain.from_iterable(self.eggs.values()) ) return itertools.chain(infos, eggs) class Prepared: """ A prepared search query for metadata on a possibly-named package. Pre-calculates the normalization to prevent repeated operations. >>> none = Prepared(None) >>> none.normalized >>> none.legacy_normalized >>> bool(none) False >>> sample = Prepared('Sample__Pkg-name.foo') >>> sample.normalized 'sample_pkg_name_foo' >>> sample.legacy_normalized 'sample__pkg_name.foo' >>> bool(sample) True """ normalized = None legacy_normalized = None def __init__(self, name: Optional[str]): self.name = name if name is None: return self.normalized = self.normalize(name) self.legacy_normalized = self.legacy_normalize(name) @staticmethod def normalize(name): """ PEP 503 normalization plus dashes as underscores. """ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') @staticmethod def legacy_normalize(name): """ Normalize the package name as found in the convention in older packaging tools versions and specs. """ return name.lower().replace('-', '_') def __bool__(self): return bool(self.name) @install class MetadataPathFinder(NullFinder, DistributionFinder): """A degenerate finder for distribution packages on the file system. This finder supplies only a find_distributions() method for versions of Python that do not have a PathFinder find_distributions(). """ @classmethod def find_distributions( cls, context=DistributionFinder.Context() ) -> Iterable[PathDistribution]: """ Find distributions. Return an iterable of all Distribution instances capable of loading the metadata for packages matching ``context.name`` (or all names if ``None`` indicated) along the paths in the list of directories ``context.path``. """ found = cls._search_paths(context.name, context.path) return map(PathDistribution, found) @classmethod def _search_paths(cls, name, paths): """Find metadata directories in paths heuristically.""" prepared = Prepared(name) return itertools.chain.from_iterable( path.search(prepared) for path in map(FastPath, paths) ) @classmethod def invalidate_caches(cls) -> None: FastPath.__new__.cache_clear() class PathDistribution(Distribution): def __init__(self, path: SimplePath) -> None: """Construct a distribution. :param path: SimplePath indicating the metadata directory. """ self._path = path def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]: with suppress( FileNotFoundError, IsADirectoryError, KeyError, NotADirectoryError, PermissionError, ): return self._path.joinpath(filename).read_text(encoding='utf-8') return None read_text.__doc__ = Distribution.read_text.__doc__ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath: return self._path.parent / path @property def _normalized_name(self): """ Performance optimization: where possible, resolve the normalized name from the file system path. """ stem = os.path.basename(str(self._path)) return ( pass_none(Prepared.normalize)(self._name_from_stem(stem)) or super()._normalized_name ) @staticmethod def _name_from_stem(stem): """ >>> PathDistribution._name_from_stem('foo-3.0.egg-info') 'foo' >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info') 'CherryPy' >>> PathDistribution._name_from_stem('face.egg-info') 'face' >>> PathDistribution._name_from_stem('foo.bar') """ filename, ext = os.path.splitext(stem) if ext not in ('.dist-info', '.egg-info'): return name, sep, rest = filename.partition('-') return name def distribution(distribution_name: str) -> Distribution: """Get the ``Distribution`` instance for the named package. :param distribution_name: The name of the distribution package as a string. :return: A ``Distribution`` instance (or subclass thereof). """ return Distribution.from_name(distribution_name) def distributions(**kwargs) -> Iterable[Distribution]: """Get all ``Distribution`` instances in the current environment. :return: An iterable of ``Distribution`` instances. """ return Distribution.discover(**kwargs) def metadata(distribution_name: str) -> _meta.PackageMetadata: """Get the metadata for the named package. :param distribution_name: The name of the distribution package to query. :return: A PackageMetadata containing the parsed metadata. """ return Distribution.from_name(distribution_name).metadata def version(distribution_name: str) -> str: """Get the version string for the named package. :param distribution_name: The name of the distribution package to query. :return: The version string for the package as defined in the package's "Version" metadata key. """ return distribution(distribution_name).version _unique = functools.partial( unique_everseen, key=py39.normalized_name, ) """ Wrapper for ``distributions`` to return unique distributions by name. """ def entry_points(**params) -> EntryPoints: """Return EntryPoint objects for all installed packages. Pass selection parameters (group or name) to filter the result to entry points matching those properties (see EntryPoints.select()). :return: EntryPoints for all installed packages. """ eps = itertools.chain.from_iterable( dist.entry_points for dist in _unique(distributions()) ) return EntryPoints(eps).select(**params) def files(distribution_name: str) -> Optional[List[PackagePath]]: """Return a list of files for the named package. :param distribution_name: The name of the distribution package to query. :return: List of files composing the distribution. """ return distribution(distribution_name).files def requires(distribution_name: str) -> Optional[List[str]]: """ Return a list of requirements for the named package. :return: An iterable of requirements, suitable for packaging.requirement.Requirement. """ return distribution(distribution_name).requires def packages_distributions() -> Mapping[str, List[str]]: """ Return a mapping of top-level packages to their distributions. >>> import collections.abc >>> pkgs = packages_distributions() >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) True """ pkg_to_dist = collections.defaultdict(list) for dist in distributions(): for pkg in _top_level_declared(dist) or _top_level_inferred(dist): pkg_to_dist[pkg].append(dist.metadata['Name']) return dict(pkg_to_dist) def _top_level_declared(dist): return (dist.read_text('top_level.txt') or '').split() def _topmost(name: PackagePath) -> Optional[str]: """ Return the top-most parent as long as there is a parent. """ top, *rest = name.parts return top if rest else None def _get_toplevel_name(name: PackagePath) -> str: """ Infer a possibly importable module name from a name presumed on sys.path. >>> _get_toplevel_name(PackagePath('foo.py')) 'foo' >>> _get_toplevel_name(PackagePath('foo')) 'foo' >>> _get_toplevel_name(PackagePath('foo.pyc')) 'foo' >>> _get_toplevel_name(PackagePath('foo/__init__.py')) 'foo' >>> _get_toplevel_name(PackagePath('foo.pth')) 'foo.pth' >>> _get_toplevel_name(PackagePath('foo.dist-info')) 'foo.dist-info' """ return _topmost(name) or ( # python/typeshed#10328 inspect.getmodulename(name) # type: ignore or str(name) ) def _top_level_inferred(dist): opt_names = set(map(_get_toplevel_name, always_iterable(dist.files))) def importable_name(name): return '.' not in name return filter(importable_name, opt_names) _adapters.py000064400000004627151061232500007064 0ustar00import functools import warnings import re import textwrap import email.message from ._text import FoldedCase from ._compat import pypy_partial # Do not remove prior to 2024-01-01 or Python 3.14 _warn = functools.partial( warnings.warn, "Implicit None on return values is deprecated and will raise KeyErrors.", DeprecationWarning, stacklevel=pypy_partial(2), ) class Message(email.message.Message): multiple_use_keys = set( map( FoldedCase, [ 'Classifier', 'Obsoletes-Dist', 'Platform', 'Project-URL', 'Provides-Dist', 'Provides-Extra', 'Requires-Dist', 'Requires-External', 'Supported-Platform', 'Dynamic', ], ) ) """ Keys that may be indicated multiple times per PEP 566. """ def __new__(cls, orig: email.message.Message): res = super().__new__(cls) vars(res).update(vars(orig)) return res def __init__(self, *args, **kwargs): self._headers = self._repair_headers() # suppress spurious error from mypy def __iter__(self): return super().__iter__() def __getitem__(self, item): """ Warn users that a ``KeyError`` can be expected when a missing key is supplied. Ref python/importlib_metadata#371. """ res = super().__getitem__(item) if res is None: _warn() return res def _repair_headers(self): def redent(value): "Correct for RFC822 indentation" if not value or '\n' not in value: return value return textwrap.dedent(' ' * 8 + value) headers = [(key, redent(value)) for key, value in vars(self)['_headers']] if self._payload: headers.append(('Description', self.get_payload())) return headers @property def json(self): """ Convert PackageMetadata to a JSON-compatible format per PEP 0566. """ def transform(key): value = self.get_all(key) if key in self.multiple_use_keys else self[key] if key == 'Keywords': value = re.split(r'\s+', value) tk = key.lower().replace('-', '_') return tk, value return dict(map(transform, map(FoldedCase, self))) _collections.py000064400000001347151061232500007573 0ustar00import collections # from jaraco.collections 3.3 class FreezableDefaultDict(collections.defaultdict): """ Often it is desirable to prevent the mutation of a default dict after its initial construction, such as to prevent mutation during iteration. >>> dd = FreezableDefaultDict(list) >>> dd[0].append('1') >>> dd.freeze() >>> dd[1] [] >>> len(dd) 1 """ def __missing__(self, key): return getattr(self, '_frozen', super().__missing__)(key) def freeze(self): self._frozen = lambda key: self.default_factory() class Pair(collections.namedtuple('Pair', 'name value')): @classmethod def parse(cls, text): return cls(*map(str.strip, text.split("=", 1))) _compat.py000064400000002442151061232500006535 0ustar00import sys import platform __all__ = ['install', 'NullFinder'] def install(cls): """ Class decorator for installation on sys.meta_path. Adds the backport DistributionFinder to sys.meta_path and attempts to disable the finder functionality of the stdlib DistributionFinder. """ sys.meta_path.append(cls()) disable_stdlib_finder() return cls def disable_stdlib_finder(): """ Give the backport primacy for discovering path-based distributions by monkey-patching the stdlib O_O. See #91 for more background for rationale on this sketchy behavior. """ def matches(finder): return getattr( finder, '__module__', None ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') for finder in filter(matches, sys.meta_path): # pragma: nocover del finder.find_distributions class NullFinder: """ A "Finder" (aka "MetaPathFinder") that never finds any modules, but may find distributions. """ @staticmethod def find_spec(*args, **kwargs): return None def pypy_partial(val): """ Adjust for variable stacklevel on partial under PyPy. Workaround for #327. """ is_pypy = platform.python_implementation() == 'PyPy' return val + is_pypy _functools.py000064400000005517151061232500007274 0ustar00import types import functools # from jaraco.functools 3.3 def method_cache(method, cache_wrapper=None): """ Wrap lru_cache to support storing the cache data in the object instances. Abstracts the common paradigm where the method explicitly saves an underscore-prefixed protected property on first call and returns that subsequently. >>> class MyClass: ... calls = 0 ... ... @method_cache ... def method(self, value): ... self.calls += 1 ... return value >>> a = MyClass() >>> a.method(3) 3 >>> for x in range(75): ... res = a.method(x) >>> a.calls 75 Note that the apparent behavior will be exactly like that of lru_cache except that the cache is stored on each instance, so values in one instance will not flush values from another, and when an instance is deleted, so are the cached values for that instance. >>> b = MyClass() >>> for x in range(35): ... res = b.method(x) >>> b.calls 35 >>> a.method(0) 0 >>> a.calls 75 Note that if method had been decorated with ``functools.lru_cache()``, a.calls would have been 76 (due to the cached value of 0 having been flushed by the 'b' instance). Clear the cache with ``.cache_clear()`` >>> a.method.cache_clear() Same for a method that hasn't yet been called. >>> c = MyClass() >>> c.method.cache_clear() Another cache wrapper may be supplied: >>> cache = functools.lru_cache(maxsize=2) >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) >>> a = MyClass() >>> a.method2() 3 Caution - do not subsequently wrap the method with another decorator, such as ``@property``, which changes the semantics of the function. See also http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ for another implementation and additional justification. """ cache_wrapper = cache_wrapper or functools.lru_cache() def wrapper(self, *args, **kwargs): # it's the first call, replace the method with a cached, bound method bound_method = types.MethodType(method, self) cached_method = cache_wrapper(bound_method) setattr(self, method.__name__, cached_method) return cached_method(*args, **kwargs) # Support cache clear even before cache has been created. wrapper.cache_clear = lambda: None return wrapper # From jaraco.functools 3.3 def pass_none(func): """ Wrap func so it's not called if its first param is None >>> print_text = pass_none(print) >>> print_text('text') text >>> print_text(None) """ @functools.wraps(func) def wrapper(param, *args, **kwargs): if param is not None: return func(param, *args, **kwargs) return wrapper _itertools.py000064400000004024151061232500007274 0ustar00from itertools import filterfalse def unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." # unique_everseen('AAAABBBCCDAABBB') --> A B C D # unique_everseen('ABBCcAD', str.lower) --> A B C D seen = set() seen_add = seen.add if key is None: for element in filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: for element in iterable: k = key(element) if k not in seen: seen_add(k) yield element # copied from more_itertools 8.8 def always_iterable(obj, base_type=(str, bytes)): """If *obj* is iterable, return an iterator over its items:: >>> obj = (1, 2, 3) >>> list(always_iterable(obj)) [1, 2, 3] If *obj* is not iterable, return a one-item iterable containing *obj*:: >>> obj = 1 >>> list(always_iterable(obj)) [1] If *obj* is ``None``, return an empty iterable: >>> obj = None >>> list(always_iterable(None)) [] By default, binary and text strings are not considered iterable:: >>> obj = 'foo' >>> list(always_iterable(obj)) ['foo'] If *base_type* is set, objects for which ``isinstance(obj, base_type)`` returns ``True`` won't be considered iterable. >>> obj = {'a': 1} >>> list(always_iterable(obj)) # Iterate over the dict's keys ['a'] >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit [{'a': 1}] Set *base_type* to ``None`` to avoid any special handling and treat objects Python considers iterable as iterable: >>> obj = 'foo' >>> list(always_iterable(obj, base_type=None)) ['f', 'o', 'o'] """ if obj is None: return iter(()) if (base_type is not None) and isinstance(obj, base_type): return iter((obj,)) try: return iter(obj) except TypeError: return iter((obj,)) _meta.py000064400000003411151061232500006175 0ustar00from __future__ import annotations import os from typing import Protocol from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload _T = TypeVar("_T") class PackageMetadata(Protocol): def __len__(self) -> int: ... # pragma: no cover def __contains__(self, item: str) -> bool: ... # pragma: no cover def __getitem__(self, key: str) -> str: ... # pragma: no cover def __iter__(self) -> Iterator[str]: ... # pragma: no cover @overload def get( self, name: str, failobj: None = None ) -> Optional[str]: ... # pragma: no cover @overload def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover # overload per python/importlib_metadata#435 @overload def get_all( self, name: str, failobj: None = None ) -> Optional[List[Any]]: ... # pragma: no cover @overload def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: """ Return all values associated with a possibly multi-valued key. """ @property def json(self) -> Dict[str, Union[str, List[str]]]: """ A JSON-compatible form of the metadata. """ class SimplePath(Protocol): """ A minimal subset of pathlib.Path required by Distribution. """ def joinpath( self, other: Union[str, os.PathLike[str]] ) -> SimplePath: ... # pragma: no cover def __truediv__( self, other: Union[str, os.PathLike[str]] ) -> SimplePath: ... # pragma: no cover @property def parent(self) -> SimplePath: ... # pragma: no cover def read_text(self, encoding=None) -> str: ... # pragma: no cover def read_bytes(self) -> bytes: ... # pragma: no cover def exists(self) -> bool: ... # pragma: no cover _text.py000064400000004166151061232500006243 0ustar00import re from ._functools import method_cache # from jaraco.text 3.5 class FoldedCase(str): """ A case insensitive string class; behaves just like str except compares equal when the only variation is case. >>> s = FoldedCase('hello world') >>> s == 'Hello World' True >>> 'Hello World' == s True >>> s != 'Hello World' False >>> s.index('O') 4 >>> s.split('O') ['hell', ' w', 'rld'] >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) ['alpha', 'Beta', 'GAMMA'] Sequence membership is straightforward. >>> "Hello World" in [s] True >>> s in ["Hello World"] True You may test for set inclusion, but candidate and elements must both be folded. >>> FoldedCase("Hello World") in {s} True >>> s in {FoldedCase("Hello World")} True String inclusion works as long as the FoldedCase object is on the right. >>> "hello" in FoldedCase("Hello World") True But not if the FoldedCase object is on the left: >>> FoldedCase('hello') in 'Hello World' False In that case, use in_: >>> FoldedCase('hello').in_('Hello World') True >>> FoldedCase('hello') > FoldedCase('Hello') False """ def __lt__(self, other): return self.lower() < other.lower() def __gt__(self, other): return self.lower() > other.lower() def __eq__(self, other): return self.lower() == other.lower() def __ne__(self, other): return self.lower() != other.lower() def __hash__(self): return hash(self.lower()) def __contains__(self, other): return super().lower().__contains__(other.lower()) def in_(self, other): "Does self appear in other?" return self in FoldedCase(other) # cache lower since it's likely to be called frequently. @method_cache def lower(self): return super().lower() def index(self, sub): return self.lower().index(sub.lower()) def split(self, splitter=' ', maxsplit=0): pattern = re.compile(re.escape(splitter), re.I) return pattern.split(self, maxsplit) diagnose.py000064400000000573151061232500006707 0ustar00import sys from . import Distribution def inspect(path): print("Inspecting", path) dists = list(Distribution.discover(path=[path])) if not dists: return print("Found", len(dists), "packages:", end=' ') print(', '.join(dist.name for dist in dists)) def run(): for path in sys.path: inspect(path) if __name__ == '__main__': run() py.typed000064400000000000151061232500006224 0ustar00